Further, the textual description for the technical indicators and TA have been extracted from the Medium.com article published by Dr. Dataman, as shared by the professor under resources for the assignment.
# Load all required libraries
library(quantmod)
library(TTR)
library(tidyverse)
library(dplyr)
library(PerformanceAnalytics)
library("IRdisplay")
options("getSymbols.warning4.0"=FALSE)
SPGI - S&P GLobal Inc. FB - Facebook Inc. AAPL - Apple Inc. Also display basic summary such as head, etc.
getSymbols(c("SPGI","FB","AAPL"))
head(SPGI)
head(FB)
head(AAPL)
df = SPGI
df2 <- df
df2$OpCl <- OpCl(df2)
df2$OpOp <- OpOp(df2)
df2$HiCl <- HiCl(df2)
df2$ClCl <- ClCl(df2)
df2$pcntOpCl1 <- Delt(Op(df2),Cl(df2),k=1)
df2$pcntOpCl2 <- Delt(Op(df2),Cl(df2),k=2)
df2$pcntOpCl3 <- Delt(Op(df2),Cl(df2),k=3)
#One period lag of the close
df2$lagCl <- Lag(Cl(df2))
df2$lag2Cl <- Lag(Cl(df2),2)
df2$lag3Cl <- Lag(Cl(df2),3)
# Move up the OpCl by one period
df2$nextOpCl <- Next(OpCl(df2))
head(df2,3)
A declining OPEN price drives down all other metrics. Also, the closing price of the previous day does not have any direct relation with the opening price of the current day.
Often we need to reduce the daily stock records to weekly or monthly stock records. The irregularities in a calendar, such as holidays, leap year, or months of the year, may drive our programming crazy.
df.monthly <- to.monthly(df)
df.monthly$month <- format(index(df.monthly),"%Y%m")
df.monthly$year <- format(index(df.monthly),"%Y")
head(df.monthly, 3)
rtn.daily <- dailyReturn(df) # returns by day
rtn.weekly <- weeklyReturn(df) # returns by week
rtn.monthly <- monthlyReturn(df) # returns by month, indexed by yearmon
# daily,weekly,monthly,quarterly, and yearly
rtn.allperiods <- allReturns(df) # note the plural
head(rtn.daily, 3)
head(rtn.weekly, 2)
head(rtn.monthly, 2)
Standard Deviation and Kurtosis¶# Generate a standard normal distribution
rn <- rnorm(100000)
print(paste0("standard deviation: ", sd(rn)))
print(paste0("Kurtosis: ", round(kurtosis(rn),2)))
options(repr.plot.width = 12, repr.plot.height = 8)
hist(rn,breaks=100,prob=TRUE)
curve(dnorm(x, mean=0, sd=1), col="darkblue", lwd=2, add=TRUE ) # Overlay a standard normal distribution
print(paste0("standard deviation: ", sd(rtn.daily)))
print(paste0("Kurtosis: ", round(kurtosis(rtn.daily),2)))
options(repr.plot.width = 12, repr.plot.height = 8)
hist(rtn.daily, breaks=100, prob=TRUE) # Make it a probability distribution
m<-mean(rtn.daily)
std<-sqrt(var(rtn.daily))
print("Mean is as below:")
m
std
# Overlay a standard normal distribution
curve(dnorm(x, mean=m, sd=std), col="darkblue", lwd=2, add=TRUE )
Here, I have displayed a standard distribution, which is the blue line, and the histogram bars represent the daily returns of SPGI, which is mpstly centered around 0.
Generally, distributions of daily stock returns are fat-tailed relative to the normal distribution we showed above.
The shape of the daily return distribution is more peaked than a symmetric, bell-shaped, standard normal distribution. It means that extreme events (a large price move) are more likely to happen than a standard normal curve, can go to more than 0.2 or less than -0.1
Here, there is a very high kurtosis, meaning that the distribution is highly fat-tailed, or may have lot of outlier values. Kurtosis is 12.79 times of a normal distribution.
# A really basic boxplot.
df$year <- format(index(df),"%Y")
df$month <- format(index(df),"%Y%m")
df3 <- data.frame(df) %>% filter(year==2019)
df3$SPGI.Volume <- as.numeric(df3$SPGI.Volume)
options(repr.plot.width = 15, repr.plot.height = 8)
# Basic plot
p <-ggplot(df3, aes(x=as.factor(month), y=SPGI.Volume))
p + geom_boxplot(fill="slateblue", alpha=0.2) + xlab("Month")
SPGI has some huge variations in terms of the monthly volume of its traded stocks
options(repr.plot.width = 15, repr.plot.height = 8)
# Change outlier, color, shape and size
p2 <- p + geom_boxplot(outlier.colour="red", outlier.shape=8,
outlier.size=1) + xlab("Month")
p2
The above Box Plot is extremely useful to find the abnormal values or anomalies, which will help discover malpractices as such insider trading, etc.
The red dots indicates the outliers, or extreme changes for that month.
options(repr.plot.width = 12, repr.plot.height = 8)
df <- SPGI
df$OpCl <- OpCl(df)
df$OpOp <- OpOp(df)
df$HiCl <- HiCl(df)
df$month <- format(index(df),"%Y%m")
df$year <- format(index(df),"%Y")
df_hiCl <- df[df$year==2019,]
boxplot(HiCl~month, data=df_hiCl, notch=TRUE,
col=(c("gold","darkgreen")),
main="Hi-Closed", xlab="Month")
boxplot(OpCl~month, data=df_hiCl, notch=TRUE,
col=(c("gold","darkgreen")),
main="Open-Closed", xlab="Month")
The above Box Plots is extremely useful to find the abnormal values or anomalies, which will help discover malpractices as such insider trading, etc.
The dots indicates the outliers, or extreme changes for that month.
options(repr.plot.width = 12, repr.plot.height = 8)
chartSeries(SPGI, subset = "last 3 months")
Over the past 3 months, the stock price has shown an increasing trend.
SPGI had a very high stock price September 2020, and then dropped significantly. The high price also resulted in a high volume of stocks traded.
options(repr.plot.width = 12, repr.plot.height = 8)
chartSeries(SPGI, subset = "2019-09::2020-08")
Over the past 1 year, the stock price has shown an increasing trend, though during March-April 2020, the stock prices fell significantly, because of the Covid-19 pandemic. However, after April 2020, the prices have been rising again.
SPGI had a very low stock price during April 2020, and that is when the volume of trade was extremely high. It has been more or less declining since then.
options(repr.plot.width = 12, repr.plot.height = 8)
candleChart(SPGI,subset = "2019-09::2020-08",multi.col=TRUE, theme='white')
Over the past 1 year, the stock price has shown an increasing trend, though during March-April 2020, the stock prices fell significantly, because of the Covid-19 pandemic. However, after April 2020, the prices have been rising again.
SPGI had a very low stock price during April 2020, and that is when the volume of trade was extremely high. It has been more or less declining since then.
options(repr.plot.width = 12, repr.plot.height = 8)
chartSeries(SPGI, theme = chartTheme("white"))
Over the past 14 years, the stock price has shown an increasing trend, though it had some dips during 2019 and 2020, where the stock prices fell significantly.
The volume has also been more or less consitent, except for Jan 2013, where we see a major spike in the volume of traded stocks. There were also small spikes during 2010.
options(repr.plot.width = 12, repr.plot.height = 8)
chartSeries(SPGI, subset = "2018-09::2020-08", TA = c(addVo(), addBBands())) #add volume and Bollinger Bands from TTR
Over the past 2 years, the stock price has shown an increasing trend, though it had a huge dip during April 2020, where the stock prices fell significantly due to the Covid-19 pandemic. Prices have been rising ever since.
The volume has also been more or less consitent, except for April 2020, where we see a major spike in the volume of traded stocks. Therer were also small spikes during late 2018.
Further, we have added the lower and upper Bollinger bands, which here show that the stock prices were more less within very less deviation from the bands, except for the Covid-19 period, where there were huge deviations.
options(repr.plot.width = 12, repr.plot.height = 8)
chartSeries(SPGI, subset = "2019-09::2020-08",bar.type='hlc',
TA = c(addSMA(n=12,col="blue"),addSMA(n=26,col="red")),
theme = chartTheme("white"))
Over the past 1 year, the stock price has more or less remained above the fast and slow moving averages. Except for April 2020, where the stock prices fell significantly due to the Covid-19 pandemic.
Also, the two moving averages intersect at various points, in which case they make the MACD = 0. They indicate that for those specific points, there is no difference between the stock prices as per the two different averages.
options(repr.plot.width = 12, repr.plot.height = 8)
chartSeries(SPGI, subset = "2019-09::2020-08",bar.type='hlc',
TA = c(addSMA(n=12,col="green"),addSMA(n=26,col="red"),
addMACD(),addRSI()),
theme = chartTheme("white"))
Over the past 1 year, the MACD and the RSI have more or less remained consistent, except for April 2020, where the stock prices fell significantly due to the Covid-19 pandemic.
Also, we see that when the two moving averages intersect at varius points, they make the MACD = 0.
options(repr.plot.width = 12, repr.plot.height = 8)
chartSeries(SPGI, subset = "2019-09::2020-08",theme="white", TA="addVo();addBBands();addCCI()")
Over the past 1 year, CCI fell largely for March-April 2020, where the stock prices fell significantly due to the Covid-19 pandemic. Also, CCI has reacted more than other indicators for SPGI stocks. There are several dips.
options(repr.plot.width = 12, repr.plot.height = 8)
chartSeries(SPGI, subset = "2019-09::2020-08",
theme="white",
TA="addVo();addBBands();addCCI();
addTA(OpCl(SPGI),col='blue', type='h') ")
Over the past 1 year, Open-Close was more or less moving in a consistend trend, but the Open-Close differed a lot, during the due to the Covid-19 pandemic.
df = FB
df2 <- df
df2$OpCl <- OpCl(df2)
df2$OpOp <- OpOp(df2)
df2$HiCl <- HiCl(df2)
df2$ClCl <- ClCl(df2)
df2$pcntOpCl1 <- Delt(Op(df2),Cl(df2),k=1)
df2$pcntOpCl2 <- Delt(Op(df2),Cl(df2),k=2)
df2$pcntOpCl3 <- Delt(Op(df2),Cl(df2),k=3)
#One period lag of the close
df2$lagCl <- Lag(Cl(df2))
df2$lag2Cl <- Lag(Cl(df2),2)
df2$lag3Cl <- Lag(Cl(df2),3)
# Move up the OpCl by one period
df2$nextOpCl <- Next(OpCl(df2))
head(df2,3)
A declining OPEN price drives down all other metrics. Also, the closing price of the previous day does not have any direct relation with the opening price of the current day.
Often we need to reduce the daily stock records to weekly or monthly stock records. The irregularities in a calendar, such as holidays, leap year, or months of the year, may drive our programming crazy.
df.monthly <- to.monthly(df)
df.monthly$month <- format(index(df.monthly),"%Y%m")
df.monthly$year <- format(index(df.monthly),"%Y")
head(df.monthly, 3)
rtn.daily <- dailyReturn(df) # returns by day
rtn.weekly <- weeklyReturn(df) # returns by week
rtn.monthly <- monthlyReturn(df) # returns by month, indexed by yearmon
# daily,weekly,monthly,quarterly, and yearly
rtn.allperiods <- allReturns(df) # note the plural
head(rtn.daily, 3)
head(rtn.weekly, 2)
head(rtn.monthly, 2)
Standard Deviation and Kurtosis¶# Generate a standard normal distribution
rn <- rnorm(100000)
print(paste0("standard deviation: ", sd(rn)))
print(paste0("Kurtosis: ", round(kurtosis(rn),2)))
options(repr.plot.width = 12, repr.plot.height = 8)
hist(rn,breaks=100,prob=TRUE)
curve(dnorm(x, mean=0, sd=1), col="darkblue", lwd=2, add=TRUE ) # Overlay a standard normal distribution
print(paste0("standard deviation: ", sd(rtn.daily)))
print(paste0("Kurtosis: ", round(kurtosis(rtn.daily),2)))
options(repr.plot.width = 12, repr.plot.height = 8)
hist(rtn.daily, breaks=100, prob=TRUE) # Make it a probability distribution
m<-mean(rtn.daily)
std<-sqrt(var(rtn.daily))
print("Mean is as below:")
m
std
# Overlay a standard normal distribution
curve(dnorm(x, mean=m, sd=std), col="darkblue", lwd=2, add=TRUE )
Here, I have displayed a standard distribution, which is the blue line, and the histogram bars represent the daily returns of FB, which is mpstly centered around 0.
Generally, distributions of daily stock returns are fat-tailed relative to the normal distribution we showed above.
The shape of the daily return distribution is more peaked than a symmetric, bell-shaped, standard normal distribution. It means that extreme events (a large price move) are more likely to happen than a standard normal curve, can go to more than 0.3 or less than -0.2
Here, there is a very high kurtosis, meaning that the distribution is highly fat-tailed, or may have lot of outlier values. Kurtosis is 19.12 times of a normal distribution.
# A really basic boxplot.
df$year <- format(index(df),"%Y")
df$month <- format(index(df),"%Y%m")
df3 <- data.frame(df) %>% filter(year==2019)
df3$FB.Volume <- as.numeric(df3$FB.Volume)
options(repr.plot.width = 15, repr.plot.height = 8)
# Basic plot
p <-ggplot(df3, aes(x=as.factor(month), y=FB.Volume))
p + geom_boxplot(fill="slateblue", alpha=0.2) + xlab("Month")
FB has some huge variations in terms of the monthly volume of its traded stocks
options(repr.plot.width = 15, repr.plot.height = 8)
# Change outlier, color, shape and size
p2 <- p + geom_boxplot(outlier.colour="red", outlier.shape=8,
outlier.size=1) + xlab("Month")
p2
The above Box Plot is extremely useful to find the abnormal values or anomalies, which will help discover malpractices as such insider trading, etc.
The red dots indicates the outliers, or extreme changes for that month.
options(repr.plot.width = 12, repr.plot.height = 8)
df <- FB
df$OpCl <- OpCl(df)
df$OpOp <- OpOp(df)
df$HiCl <- HiCl(df)
df$month <- format(index(df),"%Y%m")
df$year <- format(index(df),"%Y")
df_hiCl <- df[df$year==2019,]
boxplot(HiCl~month, data=df_hiCl, notch=TRUE,
col=(c("gold","darkgreen")),
main="Hi-Closed", xlab="Month")
boxplot(OpCl~month, data=df_hiCl, notch=TRUE,
col=(c("gold","darkgreen")),
main="Open-Closed", xlab="Month")
The above Box Plots is extremely useful to find the abnormal values or anomalies, which will help discover malpractices as such insider trading, etc.
The dots indicates the outliers, or extreme changes for that month.
options(repr.plot.width = 12, repr.plot.height = 8)
chartSeries(FB, subset = "last 3 months")
Over the past 3 months, the stock price has shown an increasing trend.
FB had a very high stock price August 2020, and then dropped significantly. The high price also resulted in a high volume of stocks traded.
options(repr.plot.width = 12, repr.plot.height = 8)
chartSeries(FB, subset = "2019-09::2020-08")
Over the past 1 year, the stock price has shown an increasing trend, though during March-April 2020, the stock prices fell significantly, because of the Covid-19 pandemic. However, after April 2020, the prices have been rising again.
FB had a very low stock price during April 2020, and that is when the volume of trade was extremely high. It has been more or less rising since then.
options(repr.plot.width = 12, repr.plot.height = 8)
candleChart(FB,subset = "2019-09::2020-08",multi.col=TRUE, theme='white')
Over the past 1 year, the stock price has shown an increasing trend, though during March-April 2020, the stock prices fell significantly, because of the Covid-19 pandemic. However, after April 2020, the prices have been rising again.
FB had a very low stock price during April 2020, and that is when the volume of trade was extremely high. It has been more or less rising since then.
options(repr.plot.width = 12, repr.plot.height = 8)
chartSeries(FB, theme = chartTheme("white"))
Over the past 8 years, the stock price has shown an increasing trend, though it had some dips during 2019 and 2020, where the stock prices fell significantly.
The volume has also been more or less consitent, except for 2013, where we see a major spike in the volume of traded stocks. There were also small spikes during 2019.
options(repr.plot.width = 12, repr.plot.height = 8)
chartSeries(FB, subset = "2018-09::2020-08", TA = c(addVo(), addBBands())) #add volume and Bollinger Bands from TTR
Over the past 2 years, the stock price has shown an increasing trend, though it had a huge dip during April 2020, where the stock prices fell significantly due to the Covid-19 pandemic. Prices have been rising ever since. There was also a small dip in December 2018.
The volume has also been more or less consitent, except for April 2020, where we see a major spike in the volume of traded stocks. Therer were also small spikes during late 2018.
Further, we have added the lower and upper Bollinger bands, which here show that the stock prices were more less within very less deviation from the bands, except for the Covid-19 period, where there were huge deviations.
options(repr.plot.width = 12, repr.plot.height = 8)
chartSeries(FB, subset = "2019-09::2020-08",bar.type='hlc',
TA = c(addSMA(n=12,col="blue"),addSMA(n=26,col="red")),
theme = chartTheme("white"))
Over the past 1 year, the stock price has more or less remained above the fast and slow moving averages. Except for April 2020, where the stock prices fell significantly due to the Covid-19 pandemic.
Also, the two moving averages intersect at various points, in which case they make the MACD = 0. They indicate that for those specific points, there is no difference between the stock prices as per the two different averages.
options(repr.plot.width = 12, repr.plot.height = 8)
chartSeries(FB, subset = "2019-09::2020-08",bar.type='hlc',
TA = c(addSMA(n=12,col="green"),addSMA(n=26,col="red"),
addMACD(),addRSI()),
theme = chartTheme("white"))
Over the past 1 year, the MACD and the RSI have more or less remained consistent, except for April 2020, where the stock prices fell significantly due to the Covid-19 pandemic.
Also, we see that when the two moving averages intersect at varius points, they make the MACD = 0.
options(repr.plot.width = 12, repr.plot.height = 8)
chartSeries(FB, subset = "2019-09::2020-08",theme="white", TA="addVo();addBBands();addCCI()")
Over the past 1 year, CCI fell largely for March-April 2020, where the stock prices fell significantly due to the Covid-19 pandemic. Also, CCI has reacted more than other indicators for FB stocks. There are several dips.
options(repr.plot.width = 12, repr.plot.height = 8)
chartSeries(FB, subset = "2019-09::2020-08",
theme="white",
TA="addVo();addBBands();addCCI();
addTA(OpCl(FB),col='blue', type='h') ")
Over the past 1 year, Open-Close was more or less moving in a consistend trend, but the Open-Close differed a lot, during the due to the Covid-19 pandemic.
df = AAPL
df2 <- df
df2$OpCl <- OpCl(df2)
df2$OpOp <- OpOp(df2)
df2$HiCl <- HiCl(df2)
df2$ClCl <- ClCl(df2)
df2$pcntOpCl1 <- Delt(Op(df2),Cl(df2),k=1)
df2$pcntOpCl2 <- Delt(Op(df2),Cl(df2),k=2)
df2$pcntOpCl3 <- Delt(Op(df2),Cl(df2),k=3)
#One period lag of the close
df2$lagCl <- Lag(Cl(df2))
df2$lag2Cl <- Lag(Cl(df2),2)
df2$lag3Cl <- Lag(Cl(df2),3)
# Move up the OpCl by one period
df2$nextOpCl <- Next(OpCl(df2))
head(df2,3)
Often we need to reduce the daily stock records to weekly or monthly stock records. The irregularities in a calendar, such as holidays, leap year, or months of the year, may drive our programming crazy.
df.monthly <- to.monthly(df)
df.monthly$month <- format(index(df.monthly),"%Y%m")
df.monthly$year <- format(index(df.monthly),"%Y")
head(df.monthly, 3)
rtn.daily <- dailyReturn(df) # returns by day
rtn.weekly <- weeklyReturn(df) # returns by week
rtn.monthly <- monthlyReturn(df) # returns by month, indexed by yearmon
# daily,weekly,monthly,quarterly, and yearly
rtn.allperiods <- allReturns(df) # note the plural
head(rtn.daily, 3)
head(rtn.weekly, 2)
head(rtn.monthly, 2)
Standard Deviation and Kurtosis¶# Generate a standard normal distribution
rn <- rnorm(100000)
print(paste0("standard deviation: ", sd(rn)))
print(paste0("Kurtosis: ", round(kurtosis(rn),2)))
options(repr.plot.width = 12, repr.plot.height = 8)
hist(rn,breaks=100,prob=TRUE)
curve(dnorm(x, mean=0, sd=1), col="darkblue", lwd=2, add=TRUE ) # Overlay a standard normal distribution
print(paste0("standard deviation: ", sd(rtn.daily)))
print(paste0("Kurtosis: ", round(kurtosis(rtn.daily),2)))
options(repr.plot.width = 12, repr.plot.height = 8)
hist(rtn.daily, breaks=100, prob=TRUE) # Make it a probability distribution
m<-mean(rtn.daily)
std<-sqrt(var(rtn.daily))
print("Mean is as below:")
m
std
# Overlay a standard normal distribution
curve(dnorm(x, mean=m, sd=std), col="darkblue", lwd=2, add=TRUE )
Here, I have displayed a standard distribution, which is the blue line, and the histogram bars represent the daily returns of SPGI, which is mpstly centered around 0.
Generally, distributions of daily stock returns are fat-tailed relative to the normal distribution we showed above.
The shape of the daily return distribution is more peaked than a symmetric, bell-shaped, standard normal distribution. It means that extreme events (a large price move) are more likely to happen than a standard normal curve, can go to more than 0.15 or less than -0.15
Here, there is a high kurtosis, meaning that the distribution is highly fat-tailed, or may have lot of outlier values. Kurtosis is 6.41 times of a normal distribution.
# A really basic boxplot.
df$year <- format(index(df),"%Y")
df$month <- format(index(df),"%Y%m")
df3 <- data.frame(df) %>% filter(year==2019)
df3$AAPL.Volume <- as.numeric(df3$AAPL.Volume)
options(repr.plot.width = 15, repr.plot.height = 8)
# Basic plot
p <-ggplot(df3, aes(x=as.factor(month), y=AAPL.Volume))
p + geom_boxplot(fill="slateblue", alpha=0.2) + xlab("Month")
AAPL has very minor variations in terms of the monthly volume of its traded stocks
options(repr.plot.width = 15, repr.plot.height = 8)
# Change outlier, color, shape and size
p2 <- p + geom_boxplot(outlier.colour="red", outlier.shape=8,
outlier.size=1) + xlab("Month")
p2
The above Box Plot is extremely useful to find the abnormal values or anomalies, which will help discover malpractices as such insider trading, etc.
The red dots indicates the outliers, or extreme changes for that month.
options(repr.plot.width = 12, repr.plot.height = 8)
df <- AAPL
df$OpCl <- OpCl(df)
df$OpOp <- OpOp(df)
df$HiCl <- HiCl(df)
df$month <- format(index(df),"%Y%m")
df$year <- format(index(df),"%Y")
df_hiCl <- df[df$year==2019,]
boxplot(HiCl~month, data=df_hiCl, notch=TRUE,
col=(c("gold","darkgreen")),
main="Hi-Closed", xlab="Month")
boxplot(OpCl~month, data=df_hiCl, notch=TRUE,
col=(c("gold","darkgreen")),
main="Open-Closed", xlab="Month")
The above Box Plots is extremely useful to find the abnormal values or anomalies, which will help discover malpractices as such insider trading, etc.
The dots indicates the outliers, or extreme changes for that month.
options(repr.plot.width = 12, repr.plot.height = 8)
chartSeries(AAPL, subset = "last 3 months")
Over the past 3 months, the stock price has shown an increasing trend.
SPGI had a very high stock price August 2020, and then dropped significantly. The high price also resulted in a high volume of stocks traded.
options(repr.plot.width = 12, repr.plot.height = 8)
chartSeries(AAPL, subset = "2019-09::2020-08")
Over the past 1 year, the stock price has shown an increasing trend, though during March-April 2020, the stock prices fell significantly, because of the Covid-19 pandemic. However, after April 2020, the prices have been rising again.
AAPL had a very low stock price during April 2020, and that is when the volume of trade was very low. It has been more or less rising since then.
options(repr.plot.width = 12, repr.plot.height = 8)
candleChart(AAPL,subset = "2019-09::2020-08",multi.col=TRUE, theme='white')
Over the past 1 year, the stock price has shown an increasing trend, though during March-April 2020, the stock prices fell significantly, because of the Covid-19 pandemic. However, after April 2020, the prices have been rising again.
AAPL had a very low stock price during April 2020, and that is when the volume of trade was low. It has been more or less rising since then.
options(repr.plot.width = 12, repr.plot.height = 8)
chartSeries(AAPL, theme = chartTheme("white"))
Over the past 8 years, the stock price has shown an increasing trend, though it had some dips during 2019 and 2020, where the stock prices fell significantly.
The volume has also been more or less declining, except for 2011, where we see a major fall in the volume of traded stocks. Therer were also small spikes during 2014.
options(repr.plot.width = 12, repr.plot.height = 8)
chartSeries(AAPL, subset = "2018-09::2020-08", TA = c(addVo(), addBBands())) #add volume and Bollinger Bands from TTR
Over the past 2 years, the stock price has shown an increasing trend, though it had a huge dip during April 2020, where the stock prices fell significantly due to the Covid-19 pandemic. Prices have been rising ever since. There was also a fall in early 2019
The volume has also been more or less consitent, except for April 2020, where we see a major fall in the volume of traded stocks.
Further, we have added the lower and upper Bollinger bands, which here show that the stock prices were more less within very less deviation from the bands, except for the Covid-19 period, where there were huge deviations.
options(repr.plot.width = 12, repr.plot.height = 8)
chartSeries(AAPL, subset = "2019-09::2020-08",bar.type='hlc',
TA = c(addSMA(n=12,col="blue"),addSMA(n=26,col="red")),
theme = chartTheme("white"))
Over the past 1 year, the stock price has more or less remained above the fast and slow moving averages. Except for April 2020, where the stock prices fell significantly due to the Covid-19 pandemic.
Also, the two moving averages intersect at various points, in which case they make the MACD = 0. They indicate that for those specific points, there is no difference between the stock prices as per the two different averages.
options(repr.plot.width = 12, repr.plot.height = 8)
chartSeries(AAPL, subset = "2019-09::2020-08",bar.type='hlc',
TA = c(addSMA(n=12,col="green"),addSMA(n=26,col="red"),
addMACD(),addRSI()),
theme = chartTheme("white"))
Over the past 1 year, the MACD and the RSI have more or less remained consistent, except for March-April 2020, where the stock prices fell significantly due to the Covid-19 pandemic.
Also, we see that when the two moving averages intersect at varius points, they make the MACD = 0.
options(repr.plot.width = 12, repr.plot.height = 8)
chartSeries(AAPL, subset = "2019-09::2020-08",theme="white", TA="addVo();addBBands();addCCI()")
Over the past 1 year, CCI fell largely for March-April 2020, where the stock prices fell significantly due to the Covid-19 pandemic. Also, CCI has reacted more than other indicators for AAPL stocks. There are several dips.
options(repr.plot.width = 12, repr.plot.height = 8)
chartSeries(AAPL, subset = "2019-09::2020-08",
theme="white",
TA="addVo();addBBands();addCCI();
addTA(OpCl(AAPL),col='blue', type='h') ")
Over the past 1 year, Open-Close was more or less moving in a consistend trend, but the Open-Close differed a lot, during the due to the Covid-19 pandemic.
SPGI - S&P Global Inc.¶Typically MACD is set as the difference between the 12-period simple moving average (SMA) and 26-period simple moving average (MACD = 12-period SMA − 26-period SMA), or “fast SMA — slow SMA”.
The MACD has a positive value whenever the 12-period SMA is above the 26-period SMA and a negative value when the 12-period SMA is below the 26-period SMA. The more distant the MACD is above or below its baseline indicates that the distance between the two SMAs is growing.
The 12-period SMA is called the “fast SMA” and the 26-period SMA is called the “slow SMA” because the 12-period SMA reacts faster to the more recent price changes, than the 26-period SMA.
The MACD function in R also computes the moving average of the MACD, called signal. The default number of periods to compute the signal is nine periods.
RSI measures the magnitude of recent price changes to evaluate overbought or oversold conditions. It is displayed as an oscillator and can have a reading from 0 to 100. The general rules are:
# Define the MACD and RSI
macd = MACD(SPGI$SPGI.Adjusted, nFast = 12, nSlow = 26, nSig = 9, maType = "SMA", percent = FALSE)
rsi = RSI(SPGI$SPGI.Adjusted, n = 14, maType = "SMA")
in-smaple analysis and testing, I will be using the dates from 'Jan 2007' up till 'Dec 2015'.¶Buy-and-hold strategy means that we buy the stock and keep it all times. So here "1" means bought and held, we will not define "0" as we are not selling the stock.
bh_strategy <- rep(1,dim(macd)[1])
# Check the output type of the strategy - Numeric
str(bh_strategy)
Also, for any NA values, we will replace it with 0, which means sold or exited the market.
strategy2 = ifelse((macd$signal < macd$macd), 1, 0)
strategy2[is.na(strategy2)] = 0
# Check the output type of the strategy
str(strategy2)
# Separate dates to be used for in-sample and out-of-sample testing
strategy2_in = strategy2["/2015-12"]
strategy2_out = strategy2["2016-01/"]
# Convert to numeric form for backtesting
strategy2_in = drop(coredata(strategy2_in))
strategy2_out = drop(coredata(strategy2_out))
Enter and stay in the market
Here, we add RSI to our strategy. An overbought stock (RSI > 70) may indicate rising price opportunities. So our strategy becomes:
strategy3 = ifelse ((macd$signal < macd$macd) & (rsi$rsi > 70), 1, 0)
strategy3[is.na(strategy3)] = 0
# Separate dates to be used for in-sample and out-of-sample testing
strategy3_in = strategy3["/2015-12"]
strategy3_out = strategy3["2016-01/"]
# Convert to numeric form for backtesting
strategy3_in = drop(coredata(strategy3_in))
strategy3_out = drop(coredata(strategy3_out))
Enter and stay in the market
An oversold stock (RSI < 30) may indicate a price rebound opportunity. So our strategy becomes:
strategy4 = ifelse ((macd$signal > macd$macd) & (rsi$rsi < 30), 1, 0)
strategy4[is.na(strategy4)]= 0
# Separate dates to be used for in-sample and out-of-sample testing
strategy4_in = strategy4["/2015-12"]
strategy4_out = strategy4["2016-01/"]
# Convert to numeric form for backtesting
strategy4_in = drop(coredata(strategy4_in))
strategy4_out = drop(coredata(strategy4_out))
The SPGI has data starting Jan 2007, till current. So, for the in-sample, out of all 14 years, I have selected the first 9 years.
The essential metrics in a backtesting include the cumulative returns, the annualized returns, the Sharpe ratio, and the annualized Sharpe ratio.
The higher a fund’s Sharpe ratio, the better a fund’s returns have been relative to the risk it has taken on. Because it uses standard deviation, the Sharpe ratio can be used to compare risk-adjusted returns across all fund categories.
The higher a fund’s standard deviation, the higher the fund’s returns need to be to earn a high Sharpe ratio. Keep in mind that even though a higher Sharpe ratio indicates a better historical risk-adjusted performance, this doesn’t necessarily translate to a lower-volatility fund.
The input “strategy” is lagged by one period. It is because when we see the daily returns, the market is already closed. Our trading has to take place on the next day.
# We will put the becktesting commands in a function
# Strategy 1 - Buy-and-hold strategy
BH_backtest <- function(df,from_date,to_date,strategy_name){
rtn.daily <- dailyReturn(df) # returns by day
trade_return <- rtn.daily[index(rtn.daily)<=to_date & index(rtn.daily)>=from_date]
cumm_return <- Return.cumulative(trade_return)
annual_return <- Return.annualized(trade_return)
summary(as.ts(trade_return))
SharpeRatio <- SharpeRatio(as.ts(trade_return), Rf = 0, p = 0.95, FUN = "StdDev")
SharpeRatioAnnualized <- SharpeRatio.annualized(trade_return, Rf = 0)
out <- as.data.frame(c(cumm_return,annual_return,SharpeRatio,SharpeRatioAnnualized))
out <- round(out,2)
colnames(out) <- strategy_name
row.names(out) <- c('Cumulative Return','Annualized Return','Sharpe Ratio','Annualized Sharpe Ratio')
return(out)
}
buy_and_hold_performance <- BH_backtest(SPGI, from_date = '2007-01-03', to_date = '2015-12-31',"Strategy1 - Buy & Hold Strategy")
buy_and_hold_performance
# Other strategies
backtest <- function(df,from_date,to_date,strategy,strategy_name){
rtn.daily <- dailyReturn(df) # returns by day
trade_return <- (rtn.daily[index(rtn.daily)<=to_date & index(rtn.daily)>=from_date]) * (lag(strategy, na.pad = FALSE))
cumm_return <- Return.cumulative(trade_return)
annual_return <- Return.annualized(trade_return)
summary(as.ts(trade_return))
SharpeRatio <- SharpeRatio(as.ts(trade_return), Rf = 0, p = 0.95, FUN = "StdDev")
SharpeRatioAnnualized <- SharpeRatio.annualized(trade_return, Rf = 0)
out <- as.data.frame(c(cumm_return,annual_return,SharpeRatio,SharpeRatioAnnualized))
out <- round(out,2)
colnames(out) <- strategy_name
row.names(out) <- c('Cumulative Return','Annualized Return','Sharpe Ratio','Annualized Sharpe Ratio')
return(out)
}
# Strategy 2
strategy2_performance <- backtest(SPGI, from_date = '2007-01-03', to_date = '2015-12-31', strategy2_in,"Strategy2 - Stay if MACD > Signal")
strategy2_performance
# Strategy 3
strategy3_performance <- backtest(SPGI, from_date = '2007-01-03', to_date = '2015-12-31', strategy3_in,"Strategy3 - Overbought")
strategy3_performance
# Strategy 4
strategy4_performance <- backtest(SPGI, from_date = '2007-01-03', to_date = '2015-12-31', strategy4_in,"Strategy4 - Oversold")
strategy4_performance
For SPGI, from the above 4 strategies, on the in-sample testing, we observe that Buy & Hold and Stay if MACD > Signal are the two best strategies, although a Sharpe ratio under 1 is considered sub-optimal.
Also, the Annualized return is higher for other strategies, but we need to estimate based on volatility, so we use Sharpe Ratio. Here, Buy & Hold seems to be the best.
Also, the Annualized Sharpe ratio is best for the two strategies. We will further verify this by backtesting on the out-of-sample data, as below.
For the out-of-sample, out of total 14 years data, I have selected the recent 5 years data.
# Strategy 1
buy_and_hold_performance <- BH_backtest(SPGI, from_date = '2016-01-01', to_date = '2020-09-25',"Strategy1 - Buy & Hold Strategy")
buy_and_hold_performance
# Strategy 2
strategy2_performance <- backtest(SPGI, from_date = '2016-01-01', to_date = '2020-09-25', strategy2_out,"Strategy2 - Stay if MACD > Signal")
strategy2_performance
# Strategy 3
strategy3_performance <- backtest(SPGI, from_date = '2016-01-01', to_date = '2020-09-25', strategy3_out,"Strategy3 - Overbought")
strategy3_performance
# Strategy 4
strategy4_performance <- backtest(SPGI, from_date = '2016-01-01', to_date = '2020-09-25', strategy4_out,"Strategy4 - Oversold")
strategy4_performance
Comparing the results with in-sample results, it is clear that Buy & Hold is the best strategy out of the two we identified. This is because, it has the highest Sharpe ratio, as well the highest Annualized Sharpe ratio.
Also, in this out-of-sample test, we see that the Sharpe ratio's of the Strategy 3 and 4 have significant improvement.
Also, the Annualized return is higher for other strategies, but we need to estimate based on volatility, so we use Sharpe Ratio. Here, Buy & Hold seems to be the best.
Considering all options, the Sharpe ratio, and the annualized Sharpe ratio, the best strategy is Buy & Hold for SPGI.
macd = MACD(FB$FB.Adjusted, nFast = 12, nSlow = 26, nSig = 9, maType = "SMA", percent = FALSE)
rsi = RSI(FB$FB.Adjusted, n = 14, maType = "SMA")
in-smaple analysis and testing, I will be using the dates from 'May 2012' up till 'Dec 2017'.¶bh_strategy <- rep(1,dim(macd)[1])
Also, for any NA values, we will replace it with 0, which means sold or exited the market.
strategy2 = ifelse((macd$signal < macd$macd), 1, 0)
strategy2[is.na(strategy2)] = 0
# Separate dates to be used for in-sample and out-of-sample testing
strategy2_in = strategy2["/2017-12"]
strategy2_out = strategy2["2018-01/"]
# Convert to numeric form for backtesting
strategy2_in = drop(coredata(strategy2_in))
strategy2_out = drop(coredata(strategy2_out))
Enter and stay in the market
Here, we add RSI to our strategy. An overbought stock (RSI > 70) may indicate rising price opportunities. So our strategy becomes:
strategy3 = ifelse ((macd$signal < macd$macd) & (rsi$rsi > 70), 1, 0)
strategy3[is.na(strategy3)] = 0
# Separate dates to be used for in-sample and out-of-sample testing
strategy3_in = strategy3["/2017-12"]
strategy3_out = strategy3["2018-01/"]
# Convert to numeric form for backtesting
strategy3_in = drop(coredata(strategy3_in))
strategy3_out = drop(coredata(strategy3_out))
Enter and stay in the market
An oversold stock (RSI < 30) may indicate a price rebound opportunity. So our strategy becomes:
strategy4 = ifelse ((macd$signal > macd$macd) & (rsi$rsi < 30), 1, 0)
strategy4[is.na(strategy4)]= 0
# Separate dates to be used for in-sample and out-of-sample testing
strategy4_in = strategy4["/2017-12"]
strategy4_out = strategy4["2018-01/"]
# Convert to numeric form for backtesting
strategy4_in = drop(coredata(strategy4_in))
strategy4_out = drop(coredata(strategy4_out))
The FB has data starting May 2012, till current. So, for the in-sample, out of all 8 years, I have selected the first 5 years.
# Strategy 1
buy_and_hold_performance <- BH_backtest(FB, from_date = '2012-05-18', to_date = '2017-12-31',"Strategy1 - Buy & Hold Strategy")
buy_and_hold_performance
# Strategy 2
strategy2_performance <- backtest(FB, from_date = '2012-05-18', to_date = '2017-12-31', strategy2_in,"Strategy2 - Stay if MACD > Signal")
strategy2_performance
# Strategy 3
strategy3_performance <- backtest(FB, from_date = '2012-05-18', to_date = '2017-12-31', strategy3_in,"Strategy3 - Overbought")
strategy3_performance
# Strategy 4
strategy4_performance <- backtest(FB, from_date = '2012-05-18', to_date = '2017-12-31', strategy4_in,"Strategy4 - Oversold")
strategy4_performance
For FB, from the above 4 strategies, we observe that Buy & Hold and Stay if MACD > Signal are the best strategies, although a Sharpe ratio under 1 is considered sub-optimal.
Also, the Annualized return is higher for other strategies, but we need to estimate based on volatility, so we use Sharpe Ratio. Here, both our suggested strategies also have good returns.
Also, the Annualized Sharpe ratio is best for the two strategies. We will further verify this by backtesting on the out-of-sample data, as below.
For the out-of-sample, out of total 8 years data, I have selected the recent 3 years data.
# Strategy 1
buy_and_hold_performance <- BH_backtest(FB, from_date = '2018-01-01', to_date = '2020-09-25',"Strategy1 - Buy & Hold Strategy")
buy_and_hold_performance
# Strategy 2
strategy2_performance <- backtest(FB, from_date = '2018-01-01', to_date = '2020-09-25', strategy2_out,"Strategy2 - Stay if MACD > Signal")
strategy2_performance
# Strategy 3
strategy3_performance <- backtest(FB, from_date = '2018-01-01', to_date = '2020-09-25', strategy3_out,"Strategy3 - Overbought")
strategy3_performance
# Strategy 4
strategy4_performance <- backtest(FB, from_date = '2018-01-01', to_date = '2020-09-25', strategy4_out,"Strategy4 - Oversold")
strategy4_performance
Comparing the results with in-sample results, it is clear that Buy & Hold is the best strategy out of the two we identified. This is because, it has the highest Sharpe ratio, as well the highest Annualized Sharpe ratio.
Also, in this out-of-sample test, we see that the Sharpe ratio of Stay if MACD > Signal declines significantly, and hence is not ideal.
Also, the Annualized return is higher for other strategies, but we need to estimate based on volatility, so we use Sharpe Ratio. Here, Buy & Hold seems to be the best.
Considering all options, the Sharpe ratio, and the annualized Sharpe ratio, the best strategy is Buy & Hold for FB.
macd = MACD(AAPL$AAPL.Adjusted, nFast = 12, nSlow = 26, nSig = 9, maType = "SMA", percent = FALSE)
rsi = RSI(AAPL$AAPL.Adjusted, n = 14, maType = "SMA")
d <- cbind(AAPL,macd,rsi)
d$SMA12 <- SMA(d$AAPL.Adjusted,12)
d$SMA26 <- SMA(d$AAPL.Adjusted,26)
d <- subset(d, select = -c(AAPL.Open,AAPL.High,AAPL.Low,AAPL.Close,AAPL.Volume))
head(d,2)
in-smaple analysis and testing, I will be using the dates from 'Jan 2007' up till 'Dec 2015'.¶bh_strategy <- rep(1,dim(macd)[1])
Also, for any NA values, we will replace it with 0, which means sold or exited the market.
strategy2 = ifelse((macd$signal < macd$macd), 1, 0)
strategy2[is.na(strategy2)] = 0
# Separate dates to be used for in-sample and out-of-sample testing
strategy2_in = strategy2["/2015-12"]
strategy2_out = strategy2["2016-01/"]
# Convert to numeric form for backtesting
strategy2_in = drop(coredata(strategy2_in))
strategy2_out = drop(coredata(strategy2_out))
Enter and stay in the market
Here, we add RSI to our strategy. An overbought stock (RSI > 70) may indicate rising price opportunities. So our strategy becomes:
strategy3 = ifelse ((macd$signal < macd$macd) & (rsi$rsi > 70), 1, 0)
strategy3[is.na(strategy3)] = 0
# Separate dates to be used for in-sample and out-of-sample testing
strategy3_in = strategy3["/2015-12"]
strategy3_out = strategy3["2016-01/"]
# Convert to numeric form for backtesting
strategy3_in = drop(coredata(strategy3_in))
strategy3_out = drop(coredata(strategy3_out))
Enter and stay in the market
An oversold stock (RSI < 30) may indicate a price rebound opportunity. So our strategy becomes:
strategy4 = ifelse ((macd$signal > macd$macd) & (rsi$rsi < 30), 1, 0)
strategy4[is.na(strategy4)]= 0
# Separate dates to be used for in-sample and out-of-sample testing
strategy4_in = strategy4["/2015-12"]
strategy4_out = strategy4["2016-01/"]
# Convert to numeric form for backtesting
strategy4_in = drop(coredata(strategy4_in))
strategy4_out = drop(coredata(strategy4_out))
The AAPL has data starting Jan 2007, till current. So, for the in-sample, out of all 14 years, I have selected the first 9 years.
# Strategy 1
buy_and_hold_performance <- BH_backtest(AAPL, from_date = '2007-01-03', to_date = '2015-12-31',"Strategy1 - Buy & Hold Strategy")
buy_and_hold_performance
# Strategy 2
strategy2_performance <- backtest(AAPL, from_date = '2007-01-03', to_date = '2015-12-31', strategy2_in,"Strategy2 - Stay if MACD > Signal")
strategy2_performance
# Strategy 3
strategy3_performance <- backtest(AAPL, from_date = '2007-01-03', to_date = '2015-12-31', strategy3_in,"Strategy3 - Overbought")
strategy3_performance
# Strategy 4
strategy4_performance <- backtest(AAPL, from_date = '2007-01-03', to_date = '2015-12-31', strategy4_in,"Strategy4 - Oversold")
strategy4_performance
For AAPL, from the above 4 strategies, we observe that Buy & Hold and Overbought are the best strategies, although a Sharpe ratio under 1 is considered sub-optimal.
Also, the Annualized return is higher for other strategies, but we need to estimate based on volatility, so we use Sharpe Ratio. But here, Buy & Hold still seems the best.
Also, the Annualized Sharpe ratio is best for the two strategies. We will further verify this by backtesting on the out-of-sample data, as below.
For the out-of-sample, out of total 14 years data, I have selected the recent 5 years data.
# Strategy 1
buy_and_hold_performance <- BH_backtest(AAPL, from_date = '2016-01-01', to_date = '2020-09-25',"Buy & Hold Strategy")
buy_and_hold_performance
# Strategy 2
strategy2_performance <- backtest(AAPL, from_date = '2016-01-01', to_date = '2020-09-25', strategy2_out,"Strategy2 - Stay if MACD > Signal")
strategy2_performance
# Strategy 3
strategy3_performance <- backtest(AAPL, from_date = '2016-01-01', to_date = '2020-09-25', strategy3_out,"Strategy3 - Overbought")
strategy3_performance
# Strategy 4
strategy4_performance <- backtest(AAPL, from_date = '2016-01-01', to_date = '2020-09-25', strategy4_out,"Strategy4 - Oversold")
strategy4_performance
Comparing the results with in-sample results, it is clear that Overbought is the best strategy out of the two we identified. This is because, it has the highest Sharpe ratio, as well the highest Annualized Sharpe ratio.
Also, in this out-of-sample test, we see that the Sharpe ratio of Stay if MACD > Signal improves significantly, and is better than Buy & Hold.
Also, the Annualized return is higher for other strategies, but we need to estimate based on volatility, so we use Sharpe Ratio.
Considering all options, the Sharpe ratio, and the annualized Sharpe ratio, the best strategy is Overbought for AAPL.